#include "switch_script_object.h"

#ifdef OS_WIN
#include <atlenc.h>
#include <io.h>
#include <ShlObj.h>

#include "icons.h"
#include "quick_launch_win.h"
#include "resource.h"
#elif defined OS_LINUX
#include <dirent.h>
#include <libgen.h>
#include <sys/stat.h>

#include "quick_launch_linux.h"
#endif

#include "log.h"
#include "stdafx.h"
#include "utils.h"

extern Log g_logger;
#ifdef OS_WIN
extern HMODULE g_moudle;
#endif

#ifdef OS_WIN
const char kSeparator = '\\';
#else
const char kSeparator = '/';
#endif

NPObject* SwitchScriptObject::Allocate(NPP npp, NPClass *aClass) {
  SwitchScriptObject* script_object = new SwitchScriptObject;
  char logs[256];
  sprintf(logs, "SwitchScriptObject this=%ld", script_object);
  g_logger.WriteLog("Allocate", logs);
  if (script_object != NULL) {
    script_object->set_plugin((PluginBase*)npp->pdata);
  }
  return script_object;
}

void SwitchScriptObject::Deallocate() {
  char logs[256];
  sprintf(logs, "SwitchScriptObject this=%ld", this);
  g_logger.WriteLog("Deallocate", logs);
  delete this;
}

void SwitchScriptObject::InitHandler() {
  FunctionItem item;
  item.function_name = "validateQuickLaunches";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::ValidateQuickLaunches);
  AddFunction(item);
  item.function_name = "deleteQuickLaunch";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::DeleteQuickLaunch);
  AddFunction(item);
  item.function_name = "generateQuickLaunch";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GenerateQuickLaunch);
  AddFunction(item);
  item.function_name = "updateQuickLaunch";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::UpdateQuickLaunch);
  AddFunction(item);
  item.function_name = "getLocalPath";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetLocalPath);
  AddFunction(item);
  item.function_name = "readLocalLaunch";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::ReadLocalLaunch);
  AddFunction(item);
  item.function_name = "startup";
  item.function_pointer = ON_INVOKEHELPER(&SwitchScriptObject::Startup);
  AddFunction(item);
  item.function_name = "validPath";
  item.function_pointer = ON_INVOKEHELPER(&SwitchScriptObject::ValidPath);
  AddFunction(item);
  item.function_name = "getDesktopFolder";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetDesktopFolder);
  AddFunction(item);
  item.function_name = "getDefaultDataPath";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetDefaultDataPath);
  AddFunction(item);
  item.function_name = "getLocalIcon";
  item.function_pointer = ON_INVOKEHELPER(&SwitchScriptObject::GetLocalIcon);
  AddFunction(item);
  item.function_name = "getQuickLaunchFolder";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetQuickLaunchFolder);
  AddFunction(item);
  item.function_name = "getStartProgramFolder";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetStartProgramFolder);
  AddFunction(item);
  item.function_name = "getUserDataFolder";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetUserDataFolder);
  AddFunction(item);
  item.function_name = "getDefaultChromeIcon";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetDefaultChromeIcon);
  AddFunction(item);
  item.function_name = "getIconDataFromQuickLaunch";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::GetIconDataFromQuickLaunch);
  AddFunction(item);
  item.function_name = "isWin7System";
  item.function_pointer = ON_INVOKEHELPER(
      &SwitchScriptObject::IsWin7System);
  AddFunction(item);
}

bool SwitchScriptObject::ValidateQuickLaunches(const NPVariant *args,
                                               uint32_t argCount,
                                               NPVariant *result) {
  if (argCount != 1 || !NPVARIANT_IS_OBJECT(args[0]))
    return false;
  NPObject* launchArray = NPVARIANT_TO_OBJECT(args[0]);
  NPIdentifier id;
  id = NPN_GetStringIdentifier("length");
  if (!id) {
    return false;
  }

  NPVariant length;
  NPVariant array_item;
  NPObject* array_object;
  NPVariant property_value;
  PluginBase* plugin = get_plugin();

  if (NPN_GetProperty(plugin->get_npp(), launchArray, id, &length)) {
    int len = 0;
    if (NPVARIANT_IS_INT32(length))
      len = NPVARIANT_TO_INT32(length);
    else if (NPVARIANT_IS_DOUBLE(length))
      len = NPVARIANT_TO_DOUBLE(length);
    for (int index = 0; index < len; index++) {
      id = NPN_GetIntIdentifier(index);
      NPN_GetProperty(plugin->get_npp(), launchArray, id, &array_item);
      array_object = NPVARIANT_TO_OBJECT(array_item);
      id = NPN_GetStringIdentifier("filePath");
      if (!id)
        continue;
      NPN_GetProperty(plugin->get_npp(), array_object, id, &property_value);
      if (NPVARIANT_IS_STRING(property_value)) {
        std::string utf8_file(
            NPVARIANT_TO_STRING(property_value).UTF8Characters,
            NPVARIANT_TO_STRING(property_value).UTF8Length);
#ifdef OS_WIN
        utils::Utf8ToAnsi file_name(utf8_file.c_str());
#else
        const char* file_name = utf8_file.c_str();
#endif
        if (access(file_name, 00) != 0) {
          id = NPN_GetStringIdentifier("isValid");
          if (!id)
            continue;
          BOOLEAN_TO_NPVARIANT(false, property_value);
          NPN_SetProperty(plugin->get_npp(), array_object, id, 
                          &property_value);
        }
      }
    }
    NPN_RetainObject(launchArray);
    OBJECT_TO_NPVARIANT(launchArray, *result);
  }

  return true;
}

bool SwitchScriptObject::DeleteQuickLaunch(const NPVariant *args,
                                           uint32_t argCount,
                                           NPVariant *result) {
  BOOLEAN_TO_NPVARIANT(false, *result);

  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;
  
  std::string file(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                   NPVARIANT_TO_STRING(args[0]).UTF8Length);

  if (quick_launch::DeleteQuickLaunch(
      get_plugin()->get_native_window(), file.c_str()))
    BOOLEAN_TO_NPVARIANT(true, *result);

  return true;
}

bool SwitchScriptObject::GenerateQuickLaunch(const NPVariant *args,
                                             uint32_t argCount,
                                             NPVariant *result) {
  if (argCount != 1 || !NPVARIANT_IS_OBJECT(args[0]))
    return false;

  QuickLaunchItem quick_launch_data;
  NPObject* quick_launch_object = NPVARIANT_TO_OBJECT(args[0]);
  PluginBase* plugin = get_plugin();
  NPP npp = plugin->get_npp();
  bool ret =  true;
  ret = GetObjectProperty(npp, quick_launch_object, "id", 
                          &quick_launch_data.id);
  ret &= GetObjectProperty(npp, quick_launch_object, "filePath",
                           &quick_launch_data.file_path);
  ret &= GetObjectProperty(npp, quick_launch_object, "fileName",
                           &quick_launch_data.file_name);
  ret &= GetObjectProperty(npp, quick_launch_object, "iconFile",
                           &quick_launch_data.icon_file);
  ret &= GetObjectProperty(npp, quick_launch_object, "iconIndex",
                           &quick_launch_data.icon_index);
  ret &= GetObjectProperty(npp, quick_launch_object, "params",
                           &quick_launch_data.params);
  ret &= GetObjectProperty(npp, quick_launch_object, "themeName",
                           &quick_launch_data.theme_name);
  ret &= GetObjectProperty(npp, quick_launch_object, "themeUrl",
                           &quick_launch_data.theme_url);
  if (!ret) {
    INT32_TO_NPVARIANT(kInvalidParameters, *result);
    return true;
  }

  // Get user data directory.
  TCHAR user_data_dir[MAX_PATH] = _T("");
  quick_launch::ResolveUserDataDir(quick_launch_data.params, user_data_dir);
  
  // If the user data directory does not exist, we create it.
  if (!quick_launch::ValidPath(user_data_dir)) {
    if (!quick_launch::CreateFolder(user_data_dir, MAX_PATH)) {
      INT32_TO_NPVARIANT(kInvalidUserDataDir, *result);
      return true;
    }
  }

  // Create Extensions directory under the user data dir.
#ifdef OS_WIN
  _tcscat(user_data_dir, _T("\\Extensions"));
#else
  strcat(user_data_dir, "/Extensions");
#endif
  if (!quick_launch::ValidPath(user_data_dir)) {
    if (!quick_launch::CreateFolder(user_data_dir, MAX_PATH)) {
      INT32_TO_NPVARIANT(kInvalidUserDataDir, *result);
      return true;
    }
  }  
  if (quick_launch_data.theme_name != "custom") {
    // Get the theme helper extension path according the module path.
    TCHAR theme_helper_dir[MAX_PATH] = _T("");
#ifdef OS_WIN
    GetModuleFileName(g_moudle, theme_helper_dir, MAX_PATH);
#else
    quick_launch::GetModuleFileName(theme_helper_dir, MAX_PATH);    
#endif
    TCHAR* backslash = _tcsrchr(theme_helper_dir, kSeparator);
    int loopcount = 2;
    do {
      *backslash = 0;
      backslash = _tcsrchr(theme_helper_dir, kSeparator);
    } while (backslash && --loopcount);

    if (!loopcount) {
#ifdef OS_WIN
      _tcscat(theme_helper_dir, _T("\\theme_helper"));
#else
      strcat(theme_helper_dir, "/theme_helper");
#endif
    }

    if (!quick_launch::ValidPath(theme_helper_dir)) {
      INT32_TO_NPVARIANT(kInvalidThemeHelperDir, *result);
      return true;
    }

    // Copy the theme helper directory to the user data directory.
    if (!quick_launch::CopyFolder(theme_helper_dir, user_data_dir)) {
      INT32_TO_NPVARIANT(kCopyThemeHelperFailed, *result);
      return true;
    }

    // Add the load-extension switch in the parameters.
    quick_launch_data.params += _T(" --load-extension=");
#ifdef OS_WIN
    _tcscat(user_data_dir, _T("\\theme_helper"));
#else
    strcat(user_data_dir, "/theme_helper");
#endif
    quick_launch_data.params += '\"';
    quick_launch_data.params += user_data_dir;
    quick_launch_data.params += '\"';

    // Copy mf.json file to manifest.json
    TCHAR source_file[MAX_PATH], dest_file[MAX_PATH];
    _tcscpy(source_file, theme_helper_dir);
    _tcscpy(dest_file, user_data_dir);
#ifdef OS_WIN
    _tcscat(source_file, _T("\\mf.json"));
    _tcscat(dest_file, _T("\\manifest.json"));
#else
    strcat(source_file, "/mf.json");
    strcat(dest_file, "/manifest.json");
#endif
    
#ifdef OS_WIN
    if (!CopyFile(source_file, dest_file, FALSE)) {
#else
    if (!quick_launch::CopyFile(source_file, dest_file)) {  
#endif
      INT32_TO_NPVARIANT(kCopyManifestFileFailed, *result);
      return true;
    }

    // Modify theme.js file, write theme name and url.
#ifdef OS_WIN
    _tcscat(user_data_dir, _T("\\theme.js"));
#else
    strcat(user_data_dir, "/theme.js");
#endif
    FILE* theme_js_file = _tfopen(user_data_dir, _T("wb"));
    if (theme_helper_dir) {
      std::string value = "var themeName = '";
      value += quick_launch_data.theme_name;
      value += "';\r\nvar themeUrl = '";
      value += quick_launch_data.theme_url;
      value += "';";
      fwrite(value.c_str(), 1, value.length(), theme_js_file);
      fflush(theme_js_file);
      fclose(theme_js_file);
    } else {
      INT32_TO_NPVARIANT(kWriteThemeJsFailed, *result);
      return true;
    }
  }

  TCHAR exefile[MAX_PATH] = _T("");
  TCHAR linkfile[MAX_PATH] = _T("");
#ifdef OS_WIN
  _stprintf(linkfile, _T("%s\\%s.lnk"), quick_launch_data.file_path.c_str(),
            quick_launch_data.file_name.c_str());
  GetModuleFileName(NULL, exefile, MAX_PATH);
#else
  sprintf(linkfile, "%s/%s.desktop", quick_launch_data.file_path.c_str(),
          quick_launch_data.file_name.c_str());
  quick_launch::GetExeFileName(exefile, MAX_PATH);
#endif
  TCHAR dest_iconfile[MAX_PATH];
  if (quick_launch_data.icon_file == _T("default")) {
    // If the icon file is default, then copy the exe file to the icon file.
#ifdef OS_WIN
    memcpy(dest_iconfile, exefile, sizeof(dest_iconfile));
#elif defined OS_LINUX
    strcpy(dest_iconfile, "google-chrome");
#endif
  } else if (!PrepareIcon(
      quick_launch_data.icon_file.c_str(), dest_iconfile)) {
    INT32_TO_NPVARIANT(kInvalidIconFile, *result);
    return true;
  }
  // If link file is existed, return failed.
  if (!update_state_ && _taccess(linkfile, 0) == 0) {
    INT32_TO_NPVARIANT(kExistsSameLinkFile, *result);
    return true;
  }

  // Now we save the link file.
  if (quick_launch::SaveLink(
      get_plugin()->get_native_window(), linkfile, exefile,
      dest_iconfile, quick_launch_data.icon_index, 
      quick_launch_data.params.c_str())) {
#ifdef OS_WIN
    utils::TCHARToUtf8 temp(linkfile);
    int size = strlen(temp.get_utf8());
#else
    int size = strlen(linkfile);
#endif
    char* ret_value = (char*)NPN_MemAlloc(size + 1);
    if (ret_value && size) {
#ifdef OS_WIN
      memcpy(ret_value, temp.get_utf8(), size + 1);
#else
      strcpy(ret_value, linkfile);
#endif
      STRINGZ_TO_NPVARIANT(ret_value, *result);
    }
  } else {
    INT32_TO_NPVARIANT(kSaveLinkFailed, *result);
    return true;
  }

  return true;
}

bool SwitchScriptObject::UpdateQuickLaunch(const NPVariant *args, 
                                           uint32_t argCount, 
                                           NPVariant *result) {
  if (argCount != 2 || !NPVARIANT_IS_STRING(args[0]) ||
      !NPVARIANT_IS_OBJECT(args[1]))
    return false;

  QuickLaunchItem quick_launch_data;
  NPObject* quick_launch_object = NPVARIANT_TO_OBJECT(args[1]);
  PluginBase* plugin = get_plugin();
  NPP npp = plugin->get_npp();
  GetObjectProperty(npp, quick_launch_object, "filePath",
                    &quick_launch_data.file_path);
  GetObjectProperty(npp, quick_launch_object, "fileName",
                    &quick_launch_data.file_name);
  GetObjectProperty(npp, quick_launch_object, "params",
                    &quick_launch_data.params);
  
  TCHAR dest_user_data_dir[MAX_PATH] = _T("");
  quick_launch::ResolveUserDataDir(quick_launch_data.params, 
                                   dest_user_data_dir);

#ifdef OS_WIN
  utils::Utf8ToTCHAR origname(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                              NPVARIANT_TO_STRING(args[0]).UTF8Length);
#else
  std::string orig_file_name(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                             NPVARIANT_TO_STRING(args[0]).UTF8Length);
  const char* origname = orig_file_name.c_str();
#endif
  TCHAR linkfile[MAX_PATH] = _T("");
#ifdef OS_WIN
  _stprintf(linkfile, _T("%s\\%s.lnk"), quick_launch_data.file_path.c_str(),
            quick_launch_data.file_name.c_str());
#else
  sprintf(linkfile, "%s/%s.desktop", quick_launch_data.file_path.c_str(),
          quick_launch_data.file_name.c_str());
#endif

  std::string path;
  std::string params;
  std::string icondata;
  std::string iconfile;
  int iconindex;
  TCHAR orig_user_data_dir[MAX_PATH] = _T("");
  if (quick_launch::ResolveLink(
      plugin->get_native_window(), origname, path, params, 
      iconfile, iconindex, icondata, false)) {
    quick_launch::ResolveUserDataDir(params, orig_user_data_dir);
  }

  bool ret = true;
  bool dir_equal = (_tcscmp(orig_user_data_dir, dest_user_data_dir) == 0);
  bool link_file_equal = (_tcscmp(origname, linkfile) == 0);

  if (!link_file_equal) {
    if (!quick_launch::DeleteQuickLaunch(
        plugin->get_native_window(), origname)) {
      INT32_TO_NPVARIANT(kDeleteOriginalLinkFailed, *result);
      return true;
    }
  }
  update_state_ = true;
  ret = GenerateQuickLaunch(&args[1], 1, result);
  update_state_ = false;

  return ret;
}

bool SwitchScriptObject::GetLocalPath(const NPVariant *args,
                                      uint32_t argCount,
                                      NPVariant *result) {
  if (argCount < 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;
  std::string dialog_title(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                           NPVARIANT_TO_STRING(args[0]).UTF8Length);
  std::string path;
  if (argCount >= 2 && NPVARIANT_IS_STRING(args[1]))
    path.append(NPVARIANT_TO_STRING(args[1]).UTF8Characters,
                NPVARIANT_TO_STRING(args[1]).UTF8Length);

  char* return_path = quick_launch::GetLocalPath(
      get_plugin()->get_native_window(), dialog_title.c_str(), path.c_str());  
  STRINGZ_TO_NPVARIANT(return_path, *result);
  return true;
}

bool SwitchScriptObject::ReadLocalLaunch(const NPVariant *args, 
                                         uint32_t argCount, 
                                         NPVariant *result) {
  NULL_TO_NPVARIANT(*result);
  TCHAR launch_file[MAX_PATH] = _T("");
  PluginBase* plugin = get_plugin();
  
  if (argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
#ifdef OS_WIN
    utils::Utf8ToTCHAR lnkfile(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                               NPVARIANT_TO_STRING(args[0]).UTF8Length);
#else
    std::string file_name(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                          NPVARIANT_TO_STRING(args[0]).UTF8Length);
    const char* lnkfile = file_name.c_str();
#endif
    _tcscpy(launch_file, lnkfile);
  } else {
    if (!quick_launch::SelectQuickLaunch(
        get_plugin()->get_native_window(), launch_file, MAX_PATH))
      return true;
  }

  std::string path;
  std::string params;
  std::string icondata;
  std::string iconfile;
  int iconindex = 0;
  if (quick_launch::ResolveLink(
      plugin->get_native_window(), launch_file, path, params, 
      iconfile, iconindex, icondata)) {    
    std::string json_string;
    std::string value = "";

    TCHAR module_name[MAX_PATH] = _T("");
#ifdef OS_WIN
    utils::Utf8ToTCHAR exe_file(path.c_str());    
    GetModuleFileName(NULL, module_name, MAX_PATH);
    if (_tcscmp(exe_file, module_name) != 0) {
      INT32_TO_NPVARIANT(kNotChromeQuickLaunch, *result);
      return true;
    }
#else
    const char* exe_file = path.c_str();
    quick_launch::GetExeFileName(module_name, MAX_PATH);
    if (strcmp(exe_file, module_name) != 0) {      
      char* dir = dirname(module_name);
      if (strncmp(exe_file, dir, strlen(dir)) == 0) {
        // Executable file is not equal plugin process name,
        // but in same path. So we check whether it is a shell script file.
        dir[strlen(dir)] = '/';
        std::string command = "file -i ";
        command += exe_file;
        FILE* p = popen(command.c_str(), "r");
        bool find_name_flag = false;
        if (p != NULL) {
          char echo_contents[256] = "";
          int count = fread(echo_contents, 1, 256, p);
          if (count > 0) {
            echo_contents[count] = 0;
          }
          pclose(p);
          if (strstr(echo_contents, "shellscript") != NULL) {
            // It is a shell script file. We read it's contents for searching
            // the module name.
            char* name = basename(module_name);
            char buffer[1025] = "";
            FILE* fp = fopen(exe_file, "r");
            if (fp) {
              while(!feof(fp)) {
                if (!fgets(buffer, sizeof(buffer), fp))
                  continue;
                if (strstr(buffer, name) != NULL) {
                  g_logger.WriteLog("shellscript", buffer);
                  find_name_flag = true;
                  break;
                }
              }
              fclose(fp);
            }
          }
        }
        if (!find_name_flag) {
          INT32_TO_NPVARIANT(kNotChromeQuickLaunch, *result);
          return true;
        }
      } else {
        INT32_TO_NPVARIANT(kNotChromeQuickLaunch, *result);
        return true;
      }
    }
#endif    

#ifdef OS_WIN
    // Modify iconfile value for front-end.
    TCHAR icon_file[MAX_PATH] = _T("");
    _tcscpy(icon_file, utils::Utf8ToTCHAR(iconfile.c_str()));
#else
    char icon_file[MAX_PATH] = "";
    strcpy(icon_file, iconfile.c_str());
#endif
    TCHAR icons_path[MAX_PATH] = _T("");
    quick_launch::GetSystemIconsPath(plugin->get_native_window(), icons_path);
#ifdef OS_WIN
    if (_tcsnicmp(icons_path, icon_file, _tcslen(icons_path)) == 0) {
      // If the icon is a system icons, we only return the icon name.
      TCHAR icon_name[MAX_PATH] = _T("");
      _tsplitpath(icon_file, NULL, NULL, icon_name, NULL);
      _tcscpy(icon_file, icon_name);
    } else if (_tcsicmp(icon_file, module_name) == 0 && iconindex == 0) {
      // If the icon file is equal to exe file, we return empty string.
      _tcscpy(icon_file, _T(""));
    }
    iconfile = utils::TCHARToUtf8(icon_file).get_utf8();
#else
    if (strncmp(icons_path, icon_file, strlen(icons_path)) == 0) {
      char icon_name[NAME_MAX] = "";
      strcpy(icon_name, basename(icon_file));
      char* dot = strrchr(icon_name, '.');
      if (dot) *dot = 0;
      iconfile = icon_name;
    } else if (strcmp(icon_file, module_name) == 0) {
      iconfile = "";
    }
#endif

    // Get user data dir.
    TCHAR user_data_dir[MAX_PATH] = _T("");
    quick_launch::ResolveUserDataDir(params, user_data_dir);
    if (_tcslen(user_data_dir) == 0) {
      char* default_path = quick_launch::GetDefaultDataPath(exe_file);
#ifdef OS_WIN
      _tcscpy(user_data_dir, utils::Utf8ToTCHAR(default_path));
#else
      strcpy(user_data_dir, default_path);
#endif
      NPN_MemFree(default_path);
    }

    // Spell extensions directory.
    TCHAR extensions_dir[MAX_PATH] = _T("");
    std::string theme_name = "default";
    _tcscpy(extensions_dir, user_data_dir);
#ifdef OS_WIN
    _tcscat(extensions_dir, _T("\\Default\\Extensions"));
#else
    strcat(extensions_dir, "/Default/Extensions");
#endif
    if (quick_launch::ValidPath(extensions_dir)) {
      GetThemeName(extensions_dir, &theme_name);
    }

    json_string = "{\"name\":\"";
#ifdef OS_WIN
    utils::TCHARToUtf8 link_file(launch_file);
    const char* start = strrchr(link_file.get_utf8(), '\\');
    const char* end = strrchr(link_file.get_utf8(), '.');
#else
    const char* start = strrchr(launch_file, '/');
    const char* end = strrchr(launch_file, '.');
#endif
    if (start && end)
      json_string.append(start + 1, end - start - 1);
    json_string += "\", \"path\": \"";
#ifdef OS_WIN
    value = link_file.get_utf8();
#else
    value = launch_file;
#endif
    PrepareJsonValue(value);
    json_string += value;
    json_string += "\", \"themeName\": \"";
    json_string += theme_name;
    json_string += "\", \"switches\": {";

    int params_len = params.length();
    bool multi_element = false;

    StateType state = kStateInvalid;
    int key_prefix_start_index = 0;
    int value_start_index = 0;
    for (int index = 0; index < params_len; index++) {
      switch(state) {
        case kStateInvalid:
        case kQuoteValueEnd:
        case kValueEnd:
          if (params[index] == '-' && 
              (index == 0 || params[index - 1] == ' ')) {
            state = kKeyPrefixStart;
            key_prefix_start_index = index;
          } else
            state = kStateInvalid;
          break;
        case kKeyPrefixStart:
          if (params[index] == '-')
            state = kKeyPrefixEnd;
          else
            state = kStateInvalid;
          break;
        case kKeyPrefixEnd:
          if (params[index] >= 'a' && params[index] <= 'z')
            state = kKeyStart;
          else
            state = kStateInvalid;
          break;
        case kKeyStart:
          if (params[index] == '=') {
            state = kKeyEnd;
            std::string key = params.substr(key_prefix_start_index, 
                                            index - key_prefix_start_index);
            PrepareJsonValue(key);
            if (multi_element)
              json_string += ", \"";
            else
              json_string += "\"";
            json_string += key;
            json_string += "\": \"";
          } else if (params[index] == ' ' || index == params_len - 1) {
            state = kValueEnd;
            std::string key = params.substr(key_prefix_start_index, 
              index - key_prefix_start_index);
            PrepareJsonValue(key);
            if (multi_element)
              json_string += ", \"";
            else
              json_string += "\"";
            json_string += key;
            json_string += "\": \"\"";
            multi_element = true;
          }
          break;
        case kKeyEnd:
          if (params[index] == '"')
            state = kQuoteValueStart;
          else if (params[index] == ' ') {
            state = kValueEnd;
            json_string += "\"";
            multi_element = true;
          } else
            state = kValueStart;
          value_start_index = index;
          break;
        case kQuoteValueStart:
          if (params[index] == '"' || index == params_len - 1) {
            state = kQuoteValueEnd;
            std::string value = params.substr(value_start_index, 
                                              index - value_start_index + 1);
            PrepareJsonValue(value);
            json_string += value;
            json_string += "\"";
            multi_element = true;
          }
          break;
        case kValueStart:
          if (params[index] == ' ' || index == params_len - 1) {
            state = kValueEnd;
            std::string value = params.substr(value_start_index, 
                                              index - value_start_index);
            PrepareJsonValue(value);
            json_string += value;
            json_string += "\"";
            multi_element = true;
          }
          break;
        default:
          state = kStateInvalid;
      }
    }

    json_string += "}, \"icon\": \"";
    json_string += icondata;
    json_string += "\", \"iconIndex\":";
    char temp[30];
    sprintf(temp, "%d", iconindex);
    json_string += temp;
    json_string += ", \"iconFile\": \"";
    PrepareJsonValue(iconfile);
    json_string += iconfile;
    json_string += "\"}";
    char* switch_params = (char*)NPN_MemAlloc(json_string.length() + 1);
    if (!switch_params)
      return false;

    strcpy(switch_params, json_string.c_str());
    STRINGZ_TO_NPVARIANT(switch_params, *result);
  }

  return true;
}

bool SwitchScriptObject::Startup(const NPVariant *args,
                                 uint32_t argCount, 
                                 NPVariant *result) {
  BOOLEAN_TO_NPVARIANT(false, *result);
  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;

  std::string filename(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                       NPVARIANT_TO_STRING(args[0]).UTF8Length);
  bool ret = quick_launch::Startup(
      get_plugin()->get_native_window(), filename.c_str());
  BOOLEAN_TO_NPVARIANT(ret, *result);
  return true;
}

bool SwitchScriptObject::ValidPath(const NPVariant *args, 
                                   uint32_t argCount, 
                                   NPVariant *result) {
  BOOLEAN_TO_NPVARIANT(false, *result);
  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;

  std::string path(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                   NPVARIANT_TO_STRING(args[0]).UTF8Length);
  BOOLEAN_TO_NPVARIANT(quick_launch::ValidPath(path.c_str()), *result);

  return true;
}

bool SwitchScriptObject::GetDesktopFolder(const NPVariant *args, 
                                          uint32_t argCount, 
                                          NPVariant *result) {
  char* path = quick_launch::GetDesktopFolder(
      get_plugin()->get_native_window());
  STRINGZ_TO_NPVARIANT(path, *result);
  return true;
}

bool SwitchScriptObject::GetQuickLaunchFolder(const NPVariant *args, 
                                              uint32_t argCount, 
                                              NPVariant *result) {
  char* path = quick_launch::GetQuickLaunchFolder(
      get_plugin()->get_native_window());
  STRINGZ_TO_NPVARIANT(path, *result);

  return true;
}

bool SwitchScriptObject::GetStartProgramFolder(const NPVariant *args, 
                                               uint32_t argCount, 
                                               NPVariant *result) {
#ifdef OS_WIN
  TCHAR start_program_folder[MAX_PATH];
  TCHAR search_folder[MAX_PATH];
  TCHAR sub_directory[MAX_PATH];
  SHGetSpecialFolderPath(
      get_plugin()->get_native_window(), start_program_folder, 
      CSIDL_PROGRAMS, 0);
  _tcscpy(search_folder, start_program_folder);
  _tcscat(search_folder, L"\\*");
  WIN32_FIND_DATA wfd;
  TCHAR exe_file[MAX_PATH];
  GetModuleFileName(NULL, exe_file, MAX_PATH);

  HANDLE handle = FindFirstFile(search_folder, &wfd);
  std::string path, params, icondata, iconfile;
  int iconindex;
  if (handle != INVALID_HANDLE_VALUE) {
    do {
      if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
          _tcscmp(wfd.cFileName, _T(".")) != 0 && 
          _tcscmp(wfd.cFileName, _T("..")) != 0) {
        _stprintf(sub_directory, _T("%s\\%s"), 
                  start_program_folder, wfd.cFileName);
        _stprintf(search_folder, _T("%s\\*.lnk"), sub_directory);
        HANDLE h = FindFirstFile(search_folder, &wfd);
        if (h != INVALID_HANDLE_VALUE) {
          do {
            _stprintf(search_folder, _T("%s\\%s"), 
                      sub_directory, wfd.cFileName);
            quick_launch::ResolveLink(
                get_plugin()->get_native_window(), search_folder, path, 
                params, iconfile, iconindex, icondata, false);
            if (path == utils::TCHARToUtf8(exe_file).get_utf8()) {
              utils::TCHARToUtf8 temp(sub_directory);
              int size = strlen(temp.get_utf8());
              char* return_value = (char*)NPN_MemAlloc(size + 1);
              strcpy(return_value, temp.get_utf8());
              STRINGZ_TO_NPVARIANT(return_value, *result);
              FindClose(h);
              FindClose(handle);
              return true;
            }
          } while (FindNextFile(h, &wfd));
          FindClose(h);
        }
      } 
    } while (FindNextFile(handle, &wfd));
    FindClose(handle);
  }
#elif defined OS_LINUX
  const char* app_folder = "/usr/share/applications";
  char* path = (char*)NPN_MemAlloc(strlen(app_folder) + 1);
  strcpy(path, app_folder);
  STRINGZ_TO_NPVARIANT(path, *result);
#endif

  return true;
}

bool SwitchScriptObject::GetUserDataFolder(const NPVariant *args, 
                                           uint32_t argCount, 
                                           NPVariant *result) {
  char* user_data_dir = quick_launch::GetUserDataFolder();
  if (user_data_dir) {
    STRINGZ_TO_NPVARIANT(user_data_dir, *result);
  }
  return true;
}

bool SwitchScriptObject::GetDefaultDataPath(const NPVariant *args, 
                                            uint32_t argCount, 
                                            NPVariant *result) {
  char* path = quick_launch::GetDefaultDataPath(); 
  STRINGZ_TO_NPVARIANT(path, *result);
  return true;
}

bool SwitchScriptObject::GetLocalIcon(const NPVariant *args, 
                                      uint32_t argCount, 
                                      NPVariant *result) {
  TCHAR image_file[MAX_PATH] = _T("");
  int ret = 0;
  int index = 0;
  if (argCount == 2 && NPVARIANT_IS_STRING(args[0])) {
    index = NPVARIANT_IS_DOUBLE(args[1]) ?
        NPVARIANT_TO_DOUBLE(args[1]) : NPVARIANT_TO_INT32(args[1]);
#ifdef OS_WIN
    utils::Utf8ToTCHAR temp(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                            NPVARIANT_TO_STRING(args[0]).UTF8Length);
#else
    std::string temp_string(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                            NPVARIANT_TO_STRING(args[0]).UTF8Length);
    const char* temp = temp_string.c_str();
#endif
    _tcscpy(image_file, temp);
    ret = 1;
  } else {
#ifdef OS_WIN
    OPENFILENAME ofn = {0};
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = get_plugin()->get_native_window();
    ofn.lpstrFilter = _T("Image Files\0*.jpg;*.ico;*.png;*.bmp\0\0");
    ofn.lpstrFile = image_file;
    ofn.nMaxFile = sizeof(image_file);
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
    ofn.lpstrTitle = _T("Select Image");
    ofn.lpstrDefExt = _T("ico");

    if (GetOpenFileName(&ofn)) {
      TCHAR extname[10];
      TCHAR image_name[MAX_PATH];
      _tsplitpath(image_file, NULL, NULL, image_name, extname);
      if (_tcsicmp(extname, _T(".ico")) != 0) {
        TCHAR icon_file[MAX_PATH];
        quick_launch::GetUserDefinedIconsPath(
            get_plugin()->get_native_window(), icon_file);
        if (!quick_launch::ValidPath(icon_file))
          quick_launch::CreateFolder(icon_file, MAX_PATH);
        _tcscat(icon_file, image_name);
        _tcscat(icon_file, _T(".ico"));
        if (quick_launch::ConvertImageToIcon(image_file, icon_file)) {
          memcpy(image_file, icon_file, sizeof(image_file));
          ret = 1;
        }
      } else {
        ret = 1;
      }
    }
#else
    if (quick_launch::GetLocalImage(image_file, MAX_PATH)) {      
      char icon_file[MAX_PATH];
      quick_launch::GetUserDefinedIconsPath(
          get_plugin()->get_native_window(), icon_file);
      if (!quick_launch::ValidPath(icon_file))
        quick_launch::CreateFolder(icon_file, MAX_PATH);
      sprintf(icon_file, "%s/%s", icon_file, basename(image_file));      
      if (quick_launch::ConvertImageToIcon(image_file, icon_file)) {
        strcpy(image_file, icon_file);
        ret = 1;
      }
    }
#endif
  }
  if (ret) {
    std::string json_string = "{\"iconFile\": \"";
    std::string value;
#ifdef OS_WIN
    value = utils::TCHARToUtf8(image_file).get_utf8();
#else
    value = image_file;
#endif
    PrepareJsonValue(value);
    json_string += value;
    json_string += "\", \"index\": ";
    char temp[30];
    sprintf(temp, "%d", index);
    json_string += temp;
    json_string += ", \"iconData\": \"";
    quick_launch::GetIconData(image_file, index, json_string);
    json_string += "\"}";
    char* switch_params = (char*)NPN_MemAlloc(json_string.length() + 1);
    if (!switch_params)
      return false;

    strcpy(switch_params, json_string.c_str());
    STRINGZ_TO_NPVARIANT(switch_params, *result);
  }
  return true;
}

bool SwitchScriptObject::GetDefaultChromeIcon(const NPVariant *args, 
                                              uint32_t argCount, 
                                              NPVariant *result) {
  TCHAR exe_file[MAX_PATH] = _T("");
  std::string base64data;
#ifdef OS_WIN
  GetModuleFileName(NULL, exe_file, MAX_PATH);
#else
  quick_launch::GetDefaultChromeIcon(exe_file);
#endif
  quick_launch::GetIconData(exe_file, 0, base64data);
  char* return_value = (char*)NPN_MemAlloc(base64data.length() + 1);
  if (!return_value)
    return false;

  strcpy(return_value, base64data.c_str());
  STRINGZ_TO_NPVARIANT(return_value, *result);

  return true;
}

bool SwitchScriptObject::GetIconDataFromQuickLaunch(const NPVariant *args, 
                                                    uint32_t argCount, 
                                                    NPVariant *result) {
  if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
    return false;

  TCHAR launch_file[MAX_PATH] = _T("");
  PluginBase* plugin = get_plugin();

#ifdef OS_WIN
  utils::Utf8ToTCHAR lnkfile(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                             NPVARIANT_TO_STRING(args[0]).UTF8Length);
#else
  std::string temp_string(NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                          NPVARIANT_TO_STRING(args[0]).UTF8Length);
  const char* lnkfile = temp_string.c_str();
#endif
  _tcscpy(launch_file, lnkfile);

  std::string path;
  std::string params;
  std::string icondata;
  std::string iconfile;
  int iconindex;
  if (quick_launch::ResolveLink(
      plugin->get_native_window(), launch_file, path, params, 
      iconfile, iconindex, icondata)) {
    char* return_value = (char*)NPN_MemAlloc(icondata.length() + 1);
    if (!return_value)
      return false;
    
    strcpy(return_value, icondata.c_str());
    STRINGZ_TO_NPVARIANT(return_value, *result);
  }
  return true;
}

bool SwitchScriptObject::IsWin7System(const NPVariant *args, 
                                      uint32_t argCount, 
                                      NPVariant *result) {
  BOOLEAN_TO_NPVARIANT(false, *result);
#ifdef OS_WIN
  if (quick_launch::GetWinVersion() == quick_launch::WINVERSION_WIN7)
    BOOLEAN_TO_NPVARIANT(true, *result);
#endif
  return true;
}

void SwitchScriptObject::PrepareJsonValue(std::string& value) {
  for (int index = 0; index < value.length(); index++) {
    if (value[index] == '"' || value[index] == '\\') {
      value.insert(index, 1, '\\');
      index++;
    }
  }
}

bool SwitchScriptObject::PrepareIcon(const TCHAR* iconname, 
                                     TCHAR* dest_iconfile) {
  // If the icon name include a path. we copy it directly.
  if (_tcsrchr(iconname, kSeparator) && _taccess(iconname, 0) == 0) {
    _tcscpy(dest_iconfile, iconname);
    return true;
  }
  
  TCHAR system_icons[MAX_PATH];
  quick_launch::GetSystemIconsPath(
      get_plugin()->get_native_window(), system_icons);
  if (!quick_launch::ValidPath(system_icons))
    quick_launch::CreateFolder(system_icons, MAX_PATH);
  _tcscat(system_icons, iconname);
#ifdef OS_WIN
  if (_tcsstr(iconname,_T(".ico")) == NULL)
    _tcscat(system_icons, _T(".ico"));
#else
  if (_tcsstr(iconname,_T(".png")) == NULL)
    _tcscat(system_icons, _T(".png"));
#endif
  if (_taccess(system_icons, 0) != 0) {
    TCHAR original_name[MAX_PATH];
#ifdef OS_WIN
    GetModuleFileName(g_moudle, original_name, MAX_PATH);
#else
    quick_launch::GetModuleFileName(original_name, MAX_PATH);
#endif
    TCHAR* backslash = _tcsrchr(original_name, kSeparator);
    int loopcount = 2;
    do {
      *backslash = 0;
      backslash = _tcsrchr(original_name, kSeparator);
    } while (backslash && --loopcount);

    if (!loopcount) {
#ifdef OS_WIN
      _tcscat(original_name, _T("\\shortcut_icons\\"));
#else
      strcat(original_name, "/shortcut_icons/");
#endif
      _tcscat(original_name, iconname);
      if (_tcsstr(iconname,_T(".png")) == NULL)
        _tcscat(original_name, _T(".png"));
      if (!quick_launch::ConvertImageToIcon(original_name, system_icons))
        return false;
    }
  }
  _tcscpy(dest_iconfile, system_icons);
  if (_taccess(dest_iconfile, 0) == 0)
    return true;
  else
    return false;
}

bool SwitchScriptObject::GetObjectProperty(NPP npp, NPObject* object, 
                                           const char* name, 
                                           std::wstring* value) {
  NPIdentifier id = NPN_GetStringIdentifier(name);
  if (!id)
    return false;
  NPVariant prop_value;
  NPN_GetProperty(npp, object, id, &prop_value);
  if (NPVARIANT_IS_STRING(prop_value)) {
#ifdef OS_WIN
   *value = utils::Utf8ToUnicode(
      NPVARIANT_TO_STRING(prop_value).UTF8Characters,
      NPVARIANT_TO_STRING(prop_value).UTF8Length);
#endif
   NPN_ReleaseVariantValue(&prop_value);
  } else
   return false;
  return true;
}

bool SwitchScriptObject::GetObjectProperty(NPP npp, NPObject* object, 
                                           const char* name, 
                                           std::string* value) {
  NPIdentifier id = NPN_GetStringIdentifier(name);
  if (!id)
    return false;
  NPVariant prop_value;
  NPN_GetProperty(npp, object, id, &prop_value);
  if (NPVARIANT_IS_STRING(prop_value)) {
    *value = "";
#ifdef OS_WIN
    *value = utils::Utf8ToAnsi(NPVARIANT_TO_STRING(prop_value).UTF8Characters,
                               NPVARIANT_TO_STRING(prop_value).UTF8Length);
#else
    value->append(NPVARIANT_TO_STRING(prop_value).UTF8Characters,
                  NPVARIANT_TO_STRING(prop_value).UTF8Length);
#endif
    NPN_ReleaseVariantValue(&prop_value);
  } else
    return false;
  return true;
}

bool SwitchScriptObject::GetObjectProperty(NPP npp, NPObject* object, 
                                           const char* name, int* value) {
  NPIdentifier id = NPN_GetStringIdentifier(name);
  if (!id)
    return false;
  NPVariant prop_value;
  NPN_GetProperty(npp, object, id, &prop_value);
  if (NPVARIANT_IS_DOUBLE(prop_value) || NPVARIANT_TO_INT32(prop_value)) {
    *value = NPVARIANT_IS_DOUBLE(prop_value) ? 
        NPVARIANT_TO_DOUBLE(prop_value) : NPVARIANT_TO_INT32(prop_value);
  } else
    return false;
  return true;
}

bool SwitchScriptObject::GetThemeName(TCHAR* dir, std::string* themename) {
#ifdef OS_WIN
  TCHAR search_folder[MAX_PATH] = _T("");
  TCHAR sub_directory[MAX_PATH] = _T("");
  _tcscpy(search_folder, dir);
  _tcscat(search_folder, L"\\*");
  WIN32_FIND_DATA wfd;

  HANDLE handle = FindFirstFile(search_folder, &wfd);
  std::string path, params, icondata, iconfile;
  if (handle != INVALID_HANDLE_VALUE) {
    do {
      if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
          _tcscmp(wfd.cFileName, _T(".")) != 0 && 
          _tcscmp(wfd.cFileName, _T("..")) != 0) {
        _stprintf(sub_directory, _T("%s\\%s"), dir, wfd.cFileName);
        if (GetThemeName(sub_directory, themename))
          return true;
      } else if (_tcscmp(wfd.cFileName, _T("manifest.json")) == 0) {
        std::string json_string = "eval(";
        TCHAR manifest_name[MAX_PATH];
        _stprintf(manifest_name, _T("%s\\manifest.json"), dir); 
        char temp[1025];
        FILE* file = _tfopen(manifest_name, _T("rb"));
        if (!file)
          continue;
        int len = fread(temp, 1, 1024, file);
        while (len) {
          temp[len] = 0;
          json_string += temp;
          len = fread(temp, 1, 1024, file);
        }
        fclose(file);
        json_string += ")";
        NPObject* window;
        PluginBase* plugin = get_plugin();
        NPN_GetValue(plugin->get_npp(), NPNVWindowNPObject, &window);
        NPVariant ret;
        VOID_TO_NPVARIANT(ret);
        NPString str;
        str.UTF8Characters = json_string.c_str();
        str.UTF8Length = json_string.length();
        NPN_Evaluate(plugin->get_npp(), window, &str, &ret);
        if (NPVARIANT_IS_OBJECT(ret)) {
          NPObject* o = NPVARIANT_TO_OBJECT(ret);
          NPIdentifier id;
          id = NPN_GetStringIdentifier("theme");
          if (NPN_HasProperty(plugin->get_npp(), o, id)) {
            id = NPN_GetStringIdentifier("name");
            NPVariant name;
            NPN_GetProperty(plugin->get_npp(), o, id, &name);
            if (NPVARIANT_IS_STRING(name)) {
              *themename = "";
              themename->append(NPVARIANT_TO_STRING(name).UTF8Characters,
                                NPVARIANT_TO_STRING(name).UTF8Length);
              return true;
            }
            NPN_ReleaseVariantValue(&name);
          }
        }
        NPN_ReleaseVariantValue(&ret);
      }
    } while (FindNextFile(handle, &wfd));
    FindClose(handle);
  }
#else
  DIR *dp;
  struct dirent *entry;
  struct stat statbuf;
  char dirname[PATH_MAX];
  
  if((dp = opendir(dir)) == NULL) {    
    return false;
  }
  
  while((entry = readdir(dp)) != NULL) {
    sprintf(dirname, "%s/%s", dir, entry->d_name);    
    lstat(dirname, &statbuf);
    if(S_ISDIR(statbuf.st_mode)) {
      if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
        continue;      
      if (GetThemeName(dirname, themename))
        return true;
    } else if (strcmp(entry->d_name, "manifest.json") == 0) {
      std::string json_string = "eval(";
      char temp[1025];
      FILE* file = _tfopen(dirname, _T("rb"));
      if (!file)
        continue;
      int len = fread(temp, 1, 1024, file);
      while (len) {
        temp[len] = 0;
        json_string += temp;
        len = fread(temp, 1, 1024, file);
      }
      fclose(file);
      json_string += ")";
      NPObject* window;
      PluginBase* plugin = get_plugin();
      NPN_GetValue(plugin->get_npp(), NPNVWindowNPObject, &window);
      NPVariant ret;
      VOID_TO_NPVARIANT(ret);
      NPString str;
      str.UTF8Characters = json_string.c_str();
      str.UTF8Length = json_string.length();
      NPN_Evaluate(plugin->get_npp(), window, &str, &ret);
      if (NPVARIANT_IS_OBJECT(ret)) {
        NPObject* o = NPVARIANT_TO_OBJECT(ret);
        NPIdentifier id;
        id = NPN_GetStringIdentifier("theme");
        if (NPN_HasProperty(plugin->get_npp(), o, id)) {
          id = NPN_GetStringIdentifier("name");
          NPVariant name;
          NPN_GetProperty(plugin->get_npp(), o, id, &name);
          if (NPVARIANT_IS_STRING(name)) {
            *themename = "";
            themename->append(NPVARIANT_TO_STRING(name).UTF8Characters,
                              NPVARIANT_TO_STRING(name).UTF8Length);
            return true;
          }
          NPN_ReleaseVariantValue(&name);
        }
      }
      NPN_ReleaseVariantValue(&ret);
    }    
  }
  
  closedir(dp);
#endif
  return false;
}